﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

using SharpSoda.Common;
using SharpSoda.Message;

namespace SharpSoda.Server
{
    public class HttpLocalServer : ILocalServer
    {
        private HttpListener m_HttpServer = null;
        private Thread m_HttpListenerThread = null;
        private Uri m_Uri = null;

        private MessageDispatcher m_MsgMgr = new MessageDispatcher();

        public HttpLocalServer(string uri)
        {
            m_Uri = new Uri(uri, UriKind.Absolute);
        }

        public bool Start()
        {
            if (m_HttpServer == null)
            {
                try
                {
                    m_HttpServer = new HttpListener();

                    //m_HttpServer.Prefixes.Add(String.Format(@"http://+:{0}/", m_HttpPort));
                    m_HttpServer.Prefixes.Add(m_Uri.AbsoluteUri);

                    m_HttpServer.Start();
                    m_HttpListenerThread = new Thread(HandleHttpRequests);
                    m_HttpListenerThread.Start();

                    return true;
                }
                catch(Exception ex)
                {
                    try
                    {
                        Stop();
                    }
                    catch { }
                    m_HttpServer = null;
                    CommonLog.Error("HTTP start error: " + ex.Message);
                }
            }
            return false;
        }

        private void HandleHttpRequests()
        {
            while (m_HttpServer.IsListening)
            {
                try
                {
                    var context = m_HttpServer.BeginGetContext(new AsyncCallback(HttpListenerCallback), m_HttpServer);
                    context.AsyncWaitHandle.WaitOne();
                }
                catch { }
            }
        }

        private void HttpListenerCallback(IAsyncResult ar)
        {
            HttpListener listener = null;
            HttpListenerContext context = null;

            try
            {
                listener = ar.AsyncState as HttpListener;
                context = listener.EndGetContext(ar);
            }
            catch { }

            try
            {
                if (context != null)
                {
                    using (var reader = new StreamReader(context.Request.InputStream, context.Request.ContentEncoding))
                    {
                        string message = reader.ReadToEnd();

                        if (message != null && message.Length > 0)
                        {
                            string reply = "";
                            object result = m_MsgMgr.Process(message);
                            if (result is JsonMessage) reply = JsonHelper.ToString(result);
                            else if (result is string) reply = result as string;

                            if (reply != null && reply.Length > 0)
                            {
                                byte[] buf = Encoding.UTF8.GetBytes(reply);
                                context.Response.ContentLength64 = buf.Length;
                                context.Response.OutputStream.Write(buf, 0, buf.Length);
                                context.Response.OutputStream.Flush();
                            }

                            //context.Response.OutputStream.Close(); // do not close it so that the setting "keep-alive" may work
                        }
                    }
                    
                }
            }
            catch (Exception ex)
            {
                CommonLog.Error("HTTP process error: " + ex.Message);
            }
        }

        public void Stop()
        {
            if (m_HttpServer != null && m_HttpServer.IsListening)
            {
                m_HttpServer.Stop();
                if (m_HttpListenerThread != null) m_HttpListenerThread.Join();
                m_HttpServer.Close();
            }
        }

        public bool IsWorking()
        {
            return m_HttpServer != null && m_HttpServer.IsListening;
        }

        public Uri GetUri()
        {
            return m_Uri;
        }

        public IMessageDispatcher GetMessageDispatcher()
        {
            return m_MsgMgr;
        }
    }
}
